package com.ehome.ippusb

import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.*
import android.os.IBinder
import io.netty.bootstrap.ServerBootstrap
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.Unpooled
import io.netty.channel.*
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.handler.logging.LogLevel
import io.netty.handler.logging.LoggingHandler
import io.netty.util.CharsetUtil
import timber.log.Timber
import javax.jmdns.JmDNS
import javax.jmdns.ServiceInfo
import kotlin.concurrent.thread

class IppUsbService : Service() {

    private var mUsbManager: UsbManager? = null
    private var mIppUsbDevice: UsbDevice? = null
    private var mIppUsbInterface1: UsbInterface? = null
    private var mUsbEndpointOut1: UsbEndpoint? = null
    private var mUsbEndpointIn1: UsbEndpoint? = null

    private var mIppUsbInterface2: UsbInterface? = null
    private var mUsbEndpointOut2: UsbEndpoint? = null
    private var mUsbEndpointIn2: UsbEndpoint? = null

    var usbDeviceConnection: UsbDeviceConnection? = null

    var currentUseInterface2 = false

    var newRequest = true

    var PrintAndroidDoc:Int = 0

    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }

    override fun onCreate() {
        super.onCreate()
        mUsbManager = getSystemService(USB_SERVICE) as UsbManager
        startUsbMonitor()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        findIppUsbPrinter()

        usbDeviceConnection = mUsbManager?.openDevice(mIppUsbDevice)
        usbDeviceConnection?.claimInterface(mIppUsbInterface1, true)
        usbDeviceConnection?.setInterface(mIppUsbInterface1)

        startIpp()

        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        stopUsbMonitor()
        super.onDestroy()
    }



    private fun startIpp() {
        thread(start = true, isDaemon = true, name = "Thread-IPP") {
            Timber.d("startIpp ...")
            val bossGroup = NioEventLoopGroup(1)
//            val workerGroup = NioEventLoopGroup()
            val serverBootstrap = ServerBootstrap()
            serverBootstrap.group(bossGroup, bossGroup)
                .channel(NioServerSocketChannel::class.java)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .handler(LoggingHandler(LogLevel.INFO))

                .childHandler(object : ChannelInitializer<SocketChannel>() {
                    override fun initChannel(ch: SocketChannel?) {
                        val p = ch!!.pipeline()

                        p.addLast(object : SimpleChannelInboundHandler<Any>() {
                            override fun channelRead0(ctx: ChannelHandlerContext?, msg: Any?) {
                                val inByteBuf = msg as ByteBuf
                                val byteBufInputStream = ByteBufInputStream(inByteBuf)

                                val bufferByteArray = ByteArray(512)

                                var bufferReadCount: Int

                                while (byteBufInputStream.read(bufferByteArray).also { bufferReadCount = it } != -1) {

                                    Timber.d("准备 send usb count: ${bufferReadCount}")
                                    Timber.d("准备 send usb message: ${String(bufferByteArray!!, 0, bufferReadCount!!, Charsets.UTF_8)}")

                                    val wb = String(bufferByteArray!!, 0, bufferReadCount!!, Charsets.UTF_8)

                                    if (wb.contains("Expect: 100-continue")){
                                        send100Continue(ctx!!)
                                    }

                                    if (wb.contains("User-Agent: CUPS/2.2.9 (Linux 5.10.66; aarch64) IPP/2.0")) {
                                        if(PrintAndroidDoc >= 2){
                                            return
                                        }
                                        newRequest = false
                                        val r1 = usbDeviceConnection?.releaseInterface(mIppUsbInterface2)
                                        val r2 = usbDeviceConnection?.claimInterface(mIppUsbInterface1, true)
                                        val r3 = usbDeviceConnection?.setInterface(mIppUsbInterface1)
                                        currentUseInterface2 = false
                                        Timber.d("releaseInterface2: ${r1}, claimInterface1:${r2}, setInterface1:${r3} ")

                                    } else if (wb.contains("User-Agent: wPrintAndroidDoc")) {
                                        PrintAndroidDoc ++
                                        newRequest = false
                                        val r1 = usbDeviceConnection?.releaseInterface(mIppUsbInterface1)
                                        val r2 = usbDeviceConnection?.claimInterface(mIppUsbInterface2, true)
                                        val r3 = usbDeviceConnection?.setInterface(mIppUsbInterface2)
                                        currentUseInterface2 = true
                                        Timber.d("releaseInterface1: ${r1}, claimInterface2:${r2}, setInterface2:${r3} ")
                                    } else {
                                        if (newRequest){
                                            val r1 = usbDeviceConnection?.releaseInterface(mIppUsbInterface1)
                                            val r2 = usbDeviceConnection?.claimInterface(mIppUsbInterface2, true)
                                            val r3 = usbDeviceConnection?.setInterface(mIppUsbInterface2)
                                            currentUseInterface2 = true
                                            Timber.d("releaseInterface1: ${r1}, claimInterface2:${r2}, setInterface2:${r3} ")
                                        }else{

                                        }
                                    }


                                    val outResult = usbDeviceConnection!!.bulkTransfer(
                                        if (currentUseInterface2) mUsbEndpointOut2 else mUsbEndpointOut1,
                                        bufferByteArray,
                                        0,
                                        bufferReadCount,
                                        1000
                                    )
                                    Timber.d("已经 send usb count: ${outResult}")
                                    Timber.d("已经 send usb message: ${String(bufferByteArray!!, 0, outResult!!, Charsets.UTF_8)}")

                                    if (currentUseInterface2&&outResult<512){
                                        Timber.w("..... ...")
                                    }

                                    val s = String(bufferByteArray!!, 0, outResult!!, Charsets.UTF_8)
                                    if ((bufferReadCount < 512) and ((bufferByteArray[bufferReadCount - 1].toInt() == 0x03) or  s.endsWith("0\r\n\r\n") )) {
                                        newRequest = true
                                        Timber.d("start receive from usb ...")

                                        val list = ArrayList<ByteArray>()
                                        while (usbDeviceConnection!!.bulkTransfer(if (currentUseInterface2) mUsbEndpointIn2 else mUsbEndpointIn1, bufferByteArray, 512, 1000).also {
                                                bufferReadCount = it
                                                Timber.d("read from usb count: ${bufferReadCount}")

                                            } != -1) {
                                            val rb = String(bufferByteArray, 0, bufferReadCount)

                                            Timber.d("read from usb message: ${rb}")

                                            if (bufferReadCount != 0) {

                                                list.add(bufferByteArray.copyOfRange(0, bufferReadCount))
                                                if (rb.endsWith("0\r\n\r\n")) {
                                                    val a: Array<ByteArray> = list.toTypedArray()
                                                    ctx?.writeAndFlush(Unpooled.copiedBuffer(*a))
                                                    Timber.i("--------------------end----------------------")
                                                    break
                                                }
                                            }
                                        }
                                    }
                                }
                            }

                            override fun channelReadComplete(ctx: ChannelHandlerContext?) {
                                super.channelReadComplete(ctx)
                            }

                            override fun exceptionCaught(
                                ctx: ChannelHandlerContext?, cause: Throwable?
                            ) {
                                cause!!.printStackTrace()
                                ctx!!.close()
                            }
                        })
                    }
                })

            val channelFuture = serverBootstrap.bind(9000).sync()
        }
    }

    private fun startUsbMonitor() {
        val filter = IntentFilter()
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
//        registerReceiver(mUsbReceiver, filter)
    }

    private fun stopUsbMonitor() {
//        unregisterReceiver(mUsbReceiver);
    }

    private fun findIppUsbPrinter() {
        val list = mUsbManager?.deviceList
        var p: Pair<UsbDevice, UsbInterface>? = null
        if (list != null) {
            for ((key, device) in list) {
                Timber.d("onAttach UsbDevice -> key:${key}")
                Timber.d(device.deviceName)
                Timber.d("onAttach UsbDevice -> productName: ${device.productName}, manufacturerName: ${device.manufacturerName}")
                for (i in 0 until device.interfaceCount) {
                    val usbInterface = device.getInterface(i)
                    val interfaceClass = usbInterface.interfaceClass
                    val interfaceSubclass = usbInterface.interfaceSubclass
                    val interfaceProtocol = usbInterface.interfaceProtocol
                    Timber.d("\tinterface: ${i}, class: ${interfaceClass}, subclass: ${interfaceSubclass}, protocol: $interfaceProtocol")
                    val epCount = usbInterface.endpointCount
                    for (j in 0 until epCount) {
                        Timber.d("\t\tendpoint: ${j}, direction: ${usbInterface.getEndpoint(j).direction}")
                    }

                    if (interfaceClass == UsbConstants.USB_CLASS_PRINTER && interfaceSubclass == 1 && interfaceProtocol == 4) {
                        if (mIppUsbInterface1 == null) {
                            mIppUsbDevice = device
                            mIppUsbInterface1 = usbInterface

                            val count = mIppUsbInterface1?.endpointCount
                            for (i in 0 until count!!) {
                                val endpoint = mIppUsbInterface1?.getEndpoint(i)
                                if (UsbConstants.USB_DIR_IN == endpoint?.direction) {
                                    mUsbEndpointIn1 = endpoint
                                } else if (UsbConstants.USB_DIR_OUT == endpoint?.direction) {
                                    mUsbEndpointOut1 = endpoint
                                }
                            }
                        } else {
                            mIppUsbInterface2 = usbInterface

                            val count = mIppUsbInterface2?.endpointCount
                            for (i in 0 until count!!) {
                                val endpoint = mIppUsbInterface2?.getEndpoint(i)
                                if (UsbConstants.USB_DIR_IN == endpoint?.direction) {
                                    mUsbEndpointIn2 = endpoint
                                } else if (UsbConstants.USB_DIR_OUT == endpoint?.direction) {
                                    mUsbEndpointOut2 = endpoint
                                }
                            }
                        }

                    }
                }
            }
        }
    }


//    private val mUsbReceiver: BroadcastReceiver = object : BroadcastReceiver() {
//        override fun onReceive(context: Context, intent: Intent) {
//            val action = intent.action
//            if (UsbManager.ACTION_USB_DEVICE_ATTACHED == action) {
//                val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)
//                if (device != null) {
//                    Timber.d("onAttach UsbDevice -> productName: ${device.productName}, manufacturerName: ${device.manufacturerName}")
//                    for (i in 0 until device.interfaceCount) {
//                        val usbInterface = device.getInterface(i)
//                        val interfaceClass = usbInterface.interfaceClass
//                        val interfaceSubclass = usbInterface.interfaceSubclass
//                        val interfaceProtocol = usbInterface.interfaceProtocol
//                        Timber.d("\tinterface: ${i}, class: ${interfaceClass}, subclass: ${interfaceSubclass}, protocol: $interfaceProtocol")
//                    }
//                } else {
//                    Timber.d("onAttach UsbDevice -> device == null")
//                }
//
//            } else if (UsbManager.ACTION_USB_DEVICE_DETACHED == action) {
//                val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)
//                Timber.d("onDetach UsbDevice -> productName: ${device?.productName} , manufacturerName: ${device?.manufacturerName}")
//            }
//        }
//    }

    private fun send100Continue(ctx: ChannelHandlerContext) {
//        val response: FullHttpResponse = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE, Unpooled.EMPTY_BUFFER)
        ctx.write(Unpooled.copiedBuffer("HTTP/1.1 100 Continue\r\n\r\n", CharsetUtil.UTF_8))
    }
}

